
#include "TextureGL.hpp"
#include <stdexcept>
#include <string>
#include <sstream>

namespace Graphics
{
	using namespace RevEng;
	using namespace RevEng::Math;

	TextureGL::TextureGL() : m_nName(0), m_nPixelData(NULL), m_bLocked(false)
	{
		glGenTextures(1, &m_nName);
	}


	TextureGL::~TextureGL()
	{
		if (m_nName)
		{
			glDeleteTextures(1, &m_nName);
			m_nName = 0;
		}

		if (m_nPixelData)
		{
			delete[] m_nPixelData;
			m_nPixelData = NULL;
		}
	}


	void* TextureGL::Lock()
	{
		if (m_bLocked == false)
		{
			m_bLocked = true;
			return m_nPixelData;
		}
		else
			throw std::runtime_error("Texture already locked.");
	}


	void TextureGL::Unlock()
	{
		if (m_bLocked == true)
		{
			Size3ui Size(this->GetSize());
			GLenum eError;

			glBindTexture(GL_TEXTURE_2D, m_nName);
			glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, Size.GetWidth(), Size.GetHeight(), GL_BGR_EXT, GL_UNSIGNED_BYTE, m_nPixelData);

			eError = glGetError();

			m_bLocked = false;
		}
		else
			throw std::runtime_error("Cannot unlock if unlocked.");
	}


	Size3ui TextureGL::GetSize() const
	{
		GLint Width, Height;

		glBindTexture(GL_TEXTURE_2D, m_nName);

		glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &Width);
		glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &Height);

		return Size3ui(Width, Height, 1);
	}


	void TextureGL::SetSize( const Size3ui &Size )
	{
		GLenum eError;

		if (Size.GetVolume() == 0)
			throw std::invalid_argument("Invalid size");

		//Resize the buffer only if necessary
		if (m_nPixelData)
		{
			delete[] m_nPixelData;
			m_nPixelData = NULL;
		}

		if (m_nPixelData == NULL)
		{
			m_nPixelData = new GLubyte[3*Size.GetVolume()];
			memset(m_nPixelData, 0, 3*Size.GetVolume());
		}

		//Put these zero data on the texture
		glBindTexture(GL_TEXTURE_2D, m_nName);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, Size.GetWidth(), Size.GetHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, m_nPixelData);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

		eError = glGetError();

		if (eError != GL_NO_ERROR)
		{
			std::stringstream ss;

			ss << "Internal OpenGL texture error " << gluGetString(eError);

			throw std::runtime_error( ss.str().c_str() );
		}
	}
}
